/**
 * Copyright (C) 2013 DaiKit.com - daikit4gxt module (admin@daikit.com)
 *
 *         Project home : http://code.daikit.com/daikit4gxt
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.daikit.daikit4gxt.client.editor;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import com.google.gwt.editor.client.EditorContext;
import com.google.gwt.editor.client.EditorError;
import com.google.gwt.editor.client.EditorVisitor;
import com.google.gwt.editor.client.HasEditorErrors;


/**
 * Error collector {@link EditorVisitor}
 * 
 * @author tcaselli
 * @version $Revision$ Last modifier: $Author$ Last commit: $Date$
 */
public class DkEditorCollector extends EditorVisitor
{
	private final Stack<List<EditorError>> errorStack = new Stack<List<EditorError>>();
	private String lastAddedPath;
	private List<EditorError> lastAdded;

	/**
	 * Constructor
	 * 
	 * @param errors
	 */
	public DkEditorCollector(final List<EditorError> errors)
	{
		assert errors != null;
		errorStack.push(errors);
	}

	@Override
	public <T> void endVisit(final EditorContext<T> ctx)
	{
		final DkBeanEditorDelegate<?, ?> delegate = (DkBeanEditorDelegate<?, ?>) ctx.getEditorDelegate();
		if (delegate == null)
		{
			return;
		}

		// Collect errors
		final List<EditorError> errors = delegate.getErrors();
		lastAdded = new ArrayList<EditorError>(errors);
		lastAddedPath = ctx.getAbsolutePath();
		errorStack.peek().addAll(errors);
		errors.clear();

		// Filter collected errors through an error-aware editor
		final HasEditorErrors<T> asErrors = ctx.asHasEditorErrors();
		if (asErrors != null)
		{
			// Get the enclosing error domain
			final List<EditorError> tryConsume = errorStack.pop();
			final int prefixLength = ctx.getAbsolutePath().length();
			for (final EditorError error : tryConsume)
			{
				if (prefixLength > 0 && prefixLength < error.getAbsolutePath().length())
				{
					// Remove trailing dot
					((DkSimpleError) error).setPathPrefixLength(prefixLength + 1);
				}
				else
				{
					assert prefixLength == 0 || error.getAbsolutePath().equals(ctx.getAbsolutePath());
					((DkSimpleError) error).setPathPrefixLength(prefixLength);
				}
			}
			/*
			 * Pass collected errors to the editor. Must pass empty error collection to the editor so that it can clear any
			 * existing errors when problems are fixed.
			 */
			asErrors.showErrors(tryConsume);

			// Short-circuit if there are no existing errors
			if (!tryConsume.isEmpty())
			{
				final List<EditorError> accumulator = errorStack.peek();
				for (final EditorError e : tryConsume)
				{
					// Pass unconsumed error to enclosing domain
					if (!e.isConsumed())
					{
						accumulator.add(e);
					}
				}
			}
		}
	}

	@Override
	public <Q> boolean visit(final EditorContext<Q> ctx)
	{
		// Create a new "domain" for each error-aware editor
		final HasEditorErrors<Q> asErrors = ctx.asHasEditorErrors();
		if (asErrors != null)
		{
			/*
			 * Aliased editors (like ValueBoxEditorDecorator) will see the errors for an editor at the same path that it
			 * occupies. If the editor that we're currently looking at has the same path as the last thing we just saw,
			 * recycle the previous errors.
			 */
			if (ctx.getAbsolutePath().equals(lastAddedPath))
			{
				errorStack.peek().removeAll(lastAdded);
				errorStack.push(lastAdded);
			}
			else
			{
				errorStack.push(new ArrayList<EditorError>());
			}
		}
		return true;
	}
}
